package slowLog

import (
	"database/sql"
	"fmt"
	"db2s/dbExec"
	"db2s/global"
	"db2s/inputArg"
	"db2s/osResourceMonitor"
	"math"
	"os"
	"runtime/pprof"
	"strconv"
	"strings"
	"time"
)

func timeComple(str1, str2 string) (r int) {
	// 将字符串转换为时间对象
	layout := "2006-01-02 15:04:05"
	time1, err := time.Parse(layout, str1)
	if err != nil {
		fmt.Println("日期时间格式不匹配:", err)
		return -1
	}
	time2, err := time.Parse(layout, str2)
	if err != nil {
		fmt.Println("日期时间格式不匹配:", err)
		return -3
	}
	// 将时间对象转换为零时区（UTC）
	time1 = time1.UTC().Truncate(time.Second)
	time2 = time2.UTC().Truncate(time.Second)
	// 比较时间对象
	if time1.Before(time2) {
		r = -1
	} else if time1.After(time2) {
		r = 1
	} else {
		r = 0
	}
	return
}
func floatComple(str1, str2 string) (r int) {
	// 将字符串转换为float64类型
	num1, err := strconv.ParseFloat(str1, 64)
	if err != nil {
		fmt.Println("无法解析浮点数:", err)
		return
	}
	num2, err := strconv.ParseFloat(str2, 64)
	if err != nil {
		fmt.Println("无法解析浮点数:", err)
		return
	}

	// 定义比较的误差范围
	epsilon := 0.000001
	// 比较两个浮点数
	if math.Abs(num1-num2) < epsilon {
		r = 0
	} else if num1 < num2 {
		r = -1
	} else {
		r = 1
	}
	return
}

// conditionalFiltering 按照条件进行过滤，判断是否满足，满足则返回true
func conditionalFiltering(m *arg.Filter, c *global.HostSQL) bool {
	//判断用户是否符合
	if len(m.User) > 0 {
		var sqlGarth []*global.SlowLogEntry
		for _, v := range m.User {
			for _, v1 := range c.SqlGarth {
				if strings.EqualFold(v, v1.UserHost) {
					sqlGarth = append(sqlGarth, v1)
				}
			}
		}
		if len(sqlGarth) == 0 {
			return false
		}
		c.SqlGarth = sqlGarth
	}
	//判断查询类型是否符合
	if len(m.Type) > 0 {
		var sqlGarth []*global.SlowLogEntry
		for _, v := range m.Type {
			for _, v1 := range c.SqlGarth {
				if strings.EqualFold(v, v1.QueryType) {
					sqlGarth = append(sqlGarth, v1)
				}
			}
		}
		if len(sqlGarth) == 0 {
			return false
		}
		c.SqlGarth = sqlGarth
	}
	//判断表是否符合
	if len(m.Table) > 0 {
		var sqlGarth []*global.SlowLogEntry
		for _, v := range m.Table {
			v1 := strings.Split(v, ".")
			s1, t2 := v1[0], v1[1]
			for _, v2 := range c.SqlGarth {
				t3 := strings.ToLower(v2.QuerySql)
				n := strings.Index(t3, strings.ToLower(s1))
				n1 := strings.Index(t3, strings.ToLower(t2))
				if n != -1 || n1 != -1 {
					sqlGarth = append(sqlGarth, v2)
				}
			}
		}
		if len(sqlGarth) == 0 {
			return false
		}
		c.SqlGarth = sqlGarth
	}
	//判断时间
	if len(m.TimeLimitV.StartTime) > 0 {
		var sqlGarth []*global.SlowLogEntry
		for _, v := range c.SqlGarth {
			switch timeComple(v.Time, m.TimeLimitV.StartTime) {
			case 1:
				sqlGarth = append(sqlGarth, v)
			case 0:
				sqlGarth = append(sqlGarth, v)
			}
		}
		if len(sqlGarth) == 0 {
			return false
		}
		c.SqlGarth = sqlGarth
	}
	if len(m.TimeLimitV.EndTime) > 0 {
		var sqlGarth []*global.SlowLogEntry
		for _, v := range c.SqlGarth {
			switch timeComple(v.Time, m.TimeLimitV.EndTime) {
			case -1:
				sqlGarth = append(sqlGarth, v)
			case 0:
				sqlGarth = append(sqlGarth, v)
			}
		}
		if len(sqlGarth) == 0 {
			return false
		}
		c.SqlGarth = sqlGarth
	}
	//判断执行时间是否符合
	if m.ExecTime > 0 {
		var sqlGarth []*global.SlowLogEntry
		for _, v := range c.SqlGarth {
			switch floatComple(fmt.Sprintf("%v", m.ExecTime), v.QueryTime) {
			case 0, -1:
				sqlGarth = append(sqlGarth, v)
			}
		}
		if len(sqlGarth) == 0 {
			return false
		}
		c.SqlGarth = sqlGarth
	}
	return true
}

// 读取文件获取热sql
func getHotSqlFile(m *arg.ConfigParameter) any {
	var (
		s3 = make(chan any, 10000)
	)
	go func() {
		ll := dbExec.HotSqlInit(dbExec.HotSqlInput{
			Type: m.SecondaryL.DSnsV.SrcDBName,
			File: dbExec.ReadFile{FileName: m.SecondaryL.InspectionV.SlowLogV.InputV.FileV.Name,
				Curry: 10, RowsSplistFay: "# Time",
			},
		}).ReadFileAble()
		for {
			select {
			case c, ok := <-ll.(chan *global.HostSQL):
				if !ok {
					close(s3)
					return
				}
				if !conditionalFiltering(&m.SecondaryL.InspectionV.SlowLogV.InputV.FilterV, c) {
					continue
				}
				s3 <- c
			}
		}
	}()
	return s3
}
func TableNameParse(m *arg.ConfigParameter, a any) any {
	var (
		tableNameUniq = make(map[string]int)
		s3            = make(chan any, 10000)
	)
	go func() {
		for {
			select {
			case c, ok := <-a.(chan any):
				if !ok {
					close(s3)
					return
				} else {
					ll, err := dbExec.SqlParseInit(dbExec.SqlParseInput{
						Type: m.SecondaryL.DSnsV.SrcDBName,
						Sql:  c.(*global.HostSQL).SqlText,
					}).TableNameParse()
					if ll == nil && err != nil {
						//不支持 select * into xxx
						//不支持 select distinct distinct
						//fmt.Println("-----------lllllll:", c.(*global.HostSQL).SqlText, err)
					} else {
						for _, v := range ll.([]string) {
							var tableName string
							if !strings.Contains(v, ".") {
								tableName = strings.ToLower(fmt.Sprintf("%v.%v", c.(*global.HostSQL).SqlGarth[0].Schema, v))
							} else {
								tableName = strings.ToLower(v)
							}
							if _, ok1 := tableNameUniq[tableName]; !ok1 {
								tableNameUniq[tableName]++
								s3 <- tableName
							}
						}
					}
				}
			}
		}
	}()
	return s3
}
func analyze(m *arg.ConfigParameter, c any) {
	var (
		db  map[string]*sql.DB
		err error
	)
	if db, _, err = dbExec.OpenDb(dbExec.OpenDB{
		Dbtype:          m.SecondaryL.DSnsV.SrcDBName,
		DBDevice:        m.SecondaryL.DSnsV.SrcDrive,
		JDBC:            m.SecondaryL.DSnsV.SrcJdbc,
		MaxOpenConns:    100,
		MaxIdleConns:    100,
		ConnMaxIdleTime: 1000,
		ConnMaxLifetime: 2000,
	}).Open(false); err != nil {
		return
	}
	for {
		select {
		case s, ok := <-c.(chan any):
			if !ok {
				return
			}
			ff := dbExec.AnalyzeInit(dbExec.AnalyzeInput{
				Type:      m.SecondaryL.DSnsV.SrcDBName,
				TableName: s.(string),
			})
			if aa, err := ff.TableStruct(db["single"]); err != nil {
				return
			} else {
				fmt.Println("--------aaaa:", aa)
			}
			if aa, err := ff.TableIndex(db["single"]); err != nil {
				return
			} else {
				fmt.Println("--------bbb:", aa)
			}
		}
	}
}
func Audit(m *arg.ConfigParameter) {
	osResourceMonitor.Audit()
	f, err := os.Create("profile.pb.gz")
	if err != nil {
		return
	}
	if err = pprof.StartCPUProfile(f); err != nil {
		return
	}
	defer pprof.StopCPUProfile()
	ll := TableNameParse(m, getHotSqlFile(m))
	analyze(m, ll)

	//schemaContext(&inspection.SchemaInitContextConfig{
	//	GtCheckConfig: m,
	//})
	//schedulePlanContext(&SchedulePlanContextConfig{
	//	GtCheckConfig: *m,
	//	MQConfig:      cp.NewDefaultMq(),
	//	BeginTime:     m.BeginTime,
	//}).Check()
}
